<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Game2048</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .board {
            position: relative;
            margin: 0 auto;
            background-color: #ccc;
            border: 5px solid #ddd;
        }

        .board div {
            position: absolute;
            color: #fff;
            text-align: center;
            transition:top 0.5s ,left 0.5s;
        }
        .alert{
            text-indent:2em;
            /* 段落缩进2个字 */
        }
    </style>
</head>

<body>
        <div class="board"></div>

        <div class="alert">
                <h2>2048怎么玩？</h2>
                <p>游戏规则很简单：<strong>按键盘的方向键移动方块。</strong> 每次控制所有方块向同一个方向运动，两个相同数字的方块撞在一起之后合并成为他们的和，每次操作之后会在空白的方格处随机生成一个2或者4，最终得到一个“2048”的方块就算胜利了。如果16个格子全部填满并且相邻的格子都不相同也就是无法移动的话，那么恭喜你，gameover。</p>

                </div>
</body>
<script>
    // 利用对象的属性访问器获取对应的颜色
    var cellBackgroundColors = {
        0: '#ffeeee',
        2: '#ffccbb',
        4: '#ffbbaa',
        8: '#ffaa99',
        16: '#ffaaaa',
        32: '#ff9999',
        64: '#ff8888',
        128: '#ff7777',
        256: '#ff6666',
        512: '#ff5555',
        1024: '#ff4444',
        2048: '#ff3333',
        4096: '#ff2222',
        8192: '#ff1111'
    };
    // 游戏面板
    var board = document.getElementsByClassName('board')[0];
    // 格子间距
    var cellBorder = 10;
    // 格子宽度
    var cellWidth = 100;
    // 生成格子行数，行数列数形同使用同一个变量
    var rowNum = 4;
    // 初始活动格子的数量
    var initCellNum = 2;
    // 每次按键添加盒子数量
    var newCellNum = 1;
    // 按键对应的属性变化，属性变化未用到
    var enumDirection = { up: { key: 'top' }, down: { key: 'top' }, left: { key: 'left' }, right: { key: 'left' } };
    // 保存当前所有格子的信息
    var currentCellsState = [];

    function init() {
        //初始化背板
        initBoard();
        // 生成背景格子
        createBackgroundCells();
        // 生成活动盒子
        for (var i = 0; i < initCellNum; i++) {
            createShowCell();
        }
        // 注册按键
        addKeyDownEvents();
    }


    function initBoard() {
        board.style.width = rowNum * cellWidth + (rowNum + 1) * cellBorder + 'px';
        board.style.height = rowNum * cellWidth + (rowNum + 1) * cellBorder + 'px';
    }

    function createBackgroundCells() {
        for (var i = 0; i < rowNum; i++) {
            currentCellsState[i] = [];//初始化
            for (var j = 0; j < rowNum; j++) {
                var top = cellWidth * i + cellBorder * (i + 1);
                var left = cellWidth * j + cellBorder * (j + 1);
                createCell(0, left, top, i, j);//创建格子并添加到游戏面板中
                var cellState = {};//格子状态信息保存到全局数组中
                cellState.num = 0;//格子上的数字
                cellState.left = left;//格子左边距
                cellState.top = top;//格子上边距
                cellState.row = i;//格子所在行号
                cellState.col = j;//格子所在列号
                cellState.cell = null;//格子信息对应的格子对象
                cellState.show = false;//格子是否存在
                currentCellsState[i][j] = cellState;
            }
        }
    }
    //创建格子并添加到游戏面板中
    function createCell(num, left, top, row, col) {
        var temp = document.createElement('div');
        temp.style.left = left + 'px';
        temp.style.top = top + 'px';
        temp.style.width = cellWidth + 'px';
        temp.style.height = cellWidth + 'px';
        temp.style.lineHeight = cellWidth + 'px';
        temp.style.backgroundColor = cellBackgroundColors[num];
        temp.style.fontSize = 0.4 * cellWidth + 'px';
        temp.innerHTML = row + ',' + col;
        if (num > 0) {
            temp.innerHTML = num;//格子中的数字 大于 0 就显示出来
            currentCellsState[row][col].num = num;
            currentCellsState[row][col].show = true;
            currentCellsState[row][col].cell = temp;
        }
        board.appendChild(temp);//添加到游戏面板
        return temp;
    }

    //生成一个随机位置的盒子，并显示出来。
    function createShowCell() {
        while (true) {
            var randRow = Math.floor(Math.random() * rowNum);
            var randCol = Math.floor(Math.random() * rowNum);
            if (!currentCellsState[randRow][randCol].show) {
                var randNum = Math.random() >= 0.5 ? 2 : 4;
                var top = cellWidth * randRow + cellBorder * (randRow + 1);
                var left = cellWidth * randCol + cellBorder * (randCol + 1);
                var cell = createCell(randNum, left, top, randRow, randCol);
                console.log(cell);
                return cell;
            }
        }
    }

    // 注册按键事件
    function addKeyDownEvents() {
        document.addEventListener('keydown', function (e) {
            switch (e.key) {
                case 'ArrowUp': moveCell(enumDirection.up); break;
                case 'ArrowDown': moveCell(enumDirection.down); break;
                case 'ArrowLeft': moveCell(enumDirection.left); break;
                case 'ArrowRight': moveCell(enumDirection.right); break;
                default: break;
            }
        })
    }

    // 移动格子 一组联动的格子处理。上下以列分组，左右以行分组。
    function moveCell(direction) {
        var isChange = false;
        if (enumDirection.up == direction) {
            for (var c = 0; c < rowNum; c++) {
                for (var r = 0; r < rowNum; r++) {
                    var baseCell = currentCellsState[r][c]
                    for (var r2 = r + 1; r2 < rowNum; r2++) {
                        var tempCell = currentCellsState[r2][c]
                        var rtn = moveByCell(baseCell, tempCell);
                        isChange = rtn.isChange || isChange;
                        if (rtn.result) {
                            break;
                        }
                    }
                }
            }
        }

        if (enumDirection.down == direction) {
            for (var c = 0; c < rowNum; c++) {
                for (var r = rowNum - 1; r >= 0; r--) {
                    var baseCell = currentCellsState[r][c]
                    for (var r2 = r - 1; r2 >= 0; r2--) {
                        var tempCell = currentCellsState[r2][c]
                        var rtn = moveByCell(baseCell, tempCell);
                        isChange = rtn.isChange || isChange;
                        if (rtn.result) {
                            break;
                        }
                    }
                }
            }
        }

        if (enumDirection.left == direction) {
            for (var r = 0; r < rowNum; r++) {
                for (var c = 0; c < rowNum; c++) {
                    var baseCell = currentCellsState[r][c];
                    for (var c2 = c + 1; c2 < rowNum; c2++) {
                        var tempCell = currentCellsState[r][c2];
                        var rtn = moveByCell(baseCell, tempCell);
                        isChange = rtn.isChange || isChange;
                        if (rtn.result) {
                            break;
                        }
                    }
                }
            }
        }
        if (enumDirection.right == direction) {
            for (var r = 0; r < rowNum; r++) {
                for (var c = rowNum - 1; c >= 0; c--) {
                    var baseCell = currentCellsState[r][c];
                    for (var c2 = c - 1; c2 >= 0; c2--) {
                        var tempCell = currentCellsState[r][c2];
                        var rtn = moveByCell(baseCell, tempCell);
                        isChange = rtn.isChange || isChange;
                        if (rtn.result) {
                            break;
                        }
                    }
                }
            }
        }
        if (isChange) {
            for (var i = 0; i < newCellNum; i++) {
                createShowCell();
            }

        }
    }

    function moveByCell(baseCell, tempCell) {
        // 如果当前cell是空，则把遇到第一个cell移动到当前位置，然后继续比对，遇到相同的则合并，并返回，遇到不同的就返回。
        // 如果当前cell不为空，遇到相同的则合并，并返回，遇到不同的就返回。
        var rtn = {
            result:false,//是否中断比对。
            isChange:false//格子是否移动，如果格子移动需要添加新的盒子。未移动不添加。
        }
        if (!baseCell.show) {
            if (tempCell.show) {
                console.log('移动有数字的格子到空位', baseCell, tempCell);
                baseCell.show = true;
                baseCell.num = tempCell.num;
                baseCell.cell = tempCell.cell;
                baseCell.cell.style.left = baseCell.left + 'px';
                baseCell.cell.style.top = baseCell.top + 'px';
                // 移动完原位置清空
                tempCell.show = false;
                tempCell.num = 0;
                tempCell.cell = null;
                rtn.isChange = true;
            }
        }
        if (baseCell.show) {
            if (tempCell.show) {
                if (tempCell.num == baseCell.num) {
                    console.log('合并两个有相同数字的格子', baseCell, tempCell);
                    baseCell.num = baseCell.num * 2;
                    baseCell.cell.style.backgroundColor = cellBackgroundColors[baseCell.num];
                    baseCell.cell.innerHTML = baseCell.num;
                    // 设置移动特效，设置属性，css中设置过渡效果。
                    tempCell.cell.style.left = baseCell.left + 'px';
                    tempCell.cell.style.top = baseCell.top + 'px';
                    tempCell.show = false;
                    tempCell.num = 0;
                    // 移动完成后移除元素
                    board.removeChild(tempCell.cell);
                    tempCell.cell = null;
                    rtn.isChange = true;
                }
                rtn.result = true;
                return rtn;
            }
        }
        return rtn;
    }

    // 判断游戏结束，需要完善，未使用
    function isOver(){
        for(var r = 0; r < rowNum;r++){
            for(var c = 0; c < rowNum; c++){
                if(!currentCellsState[r][c].show){
                    return false;
                }
            }
        }
        alert('游戏结束！重新开始请刷新页面！');
    }
    init();
</script>
</html>